using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Drawing;
using Data.Probabilities;
using TreeView.Logic;
namespace Data.DataTranslator
{
    public sealed class kDimMap
    {
        public static double aggProgBar;
        public static ArrayList agglomerativeResult;
        public static ProbabilityData probData;
        private static Dictionary<int, string> propertiesNames;                 //Will hold a convertion table from code to its right property name
        private static kDimPoint[] genomProperties;                           //Each Genome has his properties over the categories
        private static Dictionary<int, string> genomeNames;
        private static int dim;
        private static int[] internalDifferentCodes;
        private static int numberOfGenomes;
        private static bool isInitialized = false;
        public kDimMap(StreamReader sReaderPropertiesCodes, StreamReader sReaderPropertiesNames, StreamReader sReaderGenomeNames)
        {
            BuildGenomeNames(sReaderGenomeNames);
            BuildGenomeProperties(sReaderPropertiesCodes);
            BuildPropertiesNames(sReaderPropertiesNames);
            isInitialized = true;
            agglomerativeResult = null;
            aggProgBar = 0;
        }
        /// <summary>
        /// Create a Dictionary thats holds (key, value) pair. key is an integer value that represents the code of
        /// This genome and value is a string that represents its name.
        /// </summary>
        /// <param name="sReaderGenomeNames"></param>
        private void BuildGenomeNames(StreamReader sReader)
        {
            Regex reg = new Regex("[0-9]+");
            MatchCollection m = reg.Matches(sReader.ReadLine());
            //Checks the number of genomes;
            if (m.Count != 1)
            {
                sReader.Close();
                throw new Exception("Bad Format");
            }
            else
            {
                kDimMap.numberOfGenomes = Int32.Parse(m[0].Value);
                kDimMap.genomeNames = new Dictionary<int, string>(kDimMap.numberOfGenomes);
                for (int i = 0; !sReader.EndOfStream; i++)
                    kDimMap.genomeNames.Add(i, sReader.ReadLine());
            }
            sReader.Close();
        }

        /// <summary>
        /// Create a dictionary for the properties names.
        /// each key is a integer number that represents the code of the property while its value is a
        /// Strign that contains the property name
        /// </summary>
        /// <param name="sReader"></param>
        private void BuildPropertiesNames(StreamReader sReader)
        {
            Regex reg = new Regex("[0-9]+");
            MatchCollection m = reg.Matches(sReader.ReadLine());            //Reads the index of the element encoding
            if (m.Count != 1)
            {
                sReader.Close();
                throw new Exception("Bad Format");
            }
            else        //We can know what is the right index for this category and we can enter the elements to the Table by their names
            {
                int numberOfPropertiesNames = Int32.Parse(m[0].Value);
                kDimMap.propertiesNames = new Dictionary<int,string>(numberOfPropertiesNames);
                for (int i=0; !sReader.EndOfStream;i++)
                    kDimMap.propertiesNames.Add(i,sReader.ReadLine());    //Saves the right string with his index(in the arrayList has its code
            }
            sReader.Close();
        }

        /// <summary>
        /// Reading the codes for each genome properties and place them in a special class calles kDimPoint.
        /// class kDimPoint saves all the properties code as a vector of integer numbers.
        /// </summary>
        /// <param name="sReader"></param>
        private void BuildGenomeProperties(StreamReader sReader)
        {
            Regex reg = new Regex("[0-9]+");
            MatchCollection m = reg.Matches(sReader.ReadLine());            //Read the number of properties and categories
            kDimMap.genomProperties = new kDimPoint[kDimMap.numberOfGenomes];
            //A parameters Thats holds the number of properties...
            kDimMap.dim = m.Count;
            //An array that holds in the kth cell the number of different codes for this category
            kDimMap.internalDifferentCodes = new int[kDimMap.dim];
            for (int i = 0; i < kDimMap.dim; i++)
                kDimMap.internalDifferentCodes[i] = Int32.Parse(m[i].Value);
            //Reading the data from the file and saves it in special format.
            for (int i = 0; !sReader.EndOfStream; i++)
            {
                m = reg.Matches(sReader.ReadLine());
                if (m.Count != kDimMap.dim)
                    throw new Exception("Bad format");
                int[] point = new int[kDimMap.dim];
                for (int j = 0; j < m.Count; j++)
                    point[j] = Int32.Parse(m[j].Value);
                kDimMap.genomProperties[i] = new kDimPoint(point);
            }
            sReader.Close();
        }  

        /// <summary>
        /// return the genome Name if and only if this class has been created before.
        /// </summary>
        /// <param name="numberOfGenome"></param>
        /// <returns></returns>
        public static string getGenomeName(int numberOfGenome)
        {
            if (isInitialized)
            {
                try
                {
                    return genomeNames[numberOfGenome];
                }
                catch (IndexOutOfRangeException e)
                {
                    System.Windows.Forms.MessageBox.Show(e.Message + " At kDimMap");
                    return null;
                }
            }
            else
                return null;
        }

        public static int getPropertyCode(int numberOfGenome, int categoryLvl)
        {
            return (genomProperties[numberOfGenome].getPoint())[categoryLvl];
        }
        public static string getPropertyName(int code)
        {
            return propertiesNames[code];
        }

        public static int getNumberOfPropertiesInCategory(int categoryLvl)
        {
            return internalDifferentCodes[categoryLvl];
        }

        /// <summary>
        /// Return a 2 dimensional ArrayList of ArrayList.
        /// The first ArrayList contains the amount of genomes in the cluster who had this property.
        /// The second AraayList contains the name of The propert.
        /// For example: Lets look on a top level division to bacteria and Archeia.
        /// Lets Say That we got 10 Bacterias and 5 Archeia Than The outPut ArrayLists will looks like that:
        /// The first one: {{10},{5}}
        /// The second one: {{"Bacteria"},{Archeia}")
        /// Please note that if there was not found any Bacterias for example than a {0} result will not be returned.
        /// </summary>
        /// <param name="genomeNumbers" an array of integers contains the codes for the genomes in the specific cluster></param>
        /// <param name="categoryLvl The Category level thats we want to get the division for. a 0 means rough diviosion while a higer number means more fragile  division"></param>
        /// <returns></returns>
        public static ArrayList[] getPropertiesDivisionOfCluster(ArrayList genomeNumbers, int categoryLvl)
        {
            //Initilize data
            //ArrayList[0] contains tha division (as a number)
            //ArrayList[1] contains the suitable name of property
            ArrayList[] ClusterProperties = new ArrayList[2];
            ClusterProperties[0] = new ArrayList(internalDifferentCodes[categoryLvl]);
            ClusterProperties[1] = new ArrayList(internalDifferentCodes[categoryLvl]);
            //The starting key that we need for this category lvl
            int startIndexOfPropertiesAtCategoryLvl = 0;
            int numberOfPropertiesInTheSameCategory;
            for (int i = 0; i < categoryLvl; i++)
                startIndexOfPropertiesAtCategoryLvl = startIndexOfPropertiesAtCategoryLvl + internalDifferentCodes[i];
            numberOfPropertiesInTheSameCategory = startIndexOfPropertiesAtCategoryLvl + internalDifferentCodes[categoryLvl];
            //Gets the names of the properties in the range [startIndexOfPropertiesAtCategoryLvl     numberOfPropertiesInTheSameCategory);   
            for (int i = startIndexOfPropertiesAtCategoryLvl; i < numberOfPropertiesInTheSameCategory; i++)
            {
                ClusterProperties[1].Add(propertiesNames[i]);
                ClusterProperties[0].Add(0.0);
            }
            //Sets the division arraylist of each property in the category number categoryLvl
            //Since this category is a number that dependes on the categoryLvl i.e. do not starts at '0' we need to offset it by startIndexOfPropertiesAtCategoryLvl
            ArrayList temp;
            double currentNUmber;
            int index;
            foreach (int gn in genomeNumbers)
            {
                index = genomProperties[(int)gn].getPoint()[categoryLvl] - startIndexOfPropertiesAtCategoryLvl;
                temp = (ArrayList)ClusterProperties[0];
                temp[index] = (double)temp[index] + 1.0;                         //currentNUmber + 1;
                //(((ArrayList)ClusterProperties[0])[genomProperties[gn].getPoint()[categoryLvl] - startIndexOfPropertiesAtCategoryLvl])++;// = temp;
            }
            //Checks for zeros and delete them from the lists.
            for (int i = 0; i < ClusterProperties[0].Count; i++)
                if ((double)ClusterProperties[0][i] == 0.0)
                {
                    ClusterProperties[0].RemoveAt(i);
                    ClusterProperties[1].RemoveAt(i);
                }
            //Trim the sizes of the ArrayList to be exacly the same size as the data they contains
            ClusterProperties[0].TrimToSize();
            ClusterProperties[1].TrimToSize();
            return ClusterProperties;
        }

        public static string[] getPropertiesOfGenome(int genomeNumber)
        {
            string[] prperties = new String[dim];
            for (int i = 0; i < dim; i++)
                prperties[i] = propertiesNames[genomProperties[genomeNumber].getValueInPointAtPosition(i)];
            return prperties;
        }


        // ============================================================================
        // HLStoRGB
        //
        // Converts the given HLS color value to its RGB equivalent and returns it.
        //
        //   H   Hue          from 0 (red) over 80 (green) and 160 (blue) to 240 (red)
        //   L   Lightness    from 0 (black) over 120 (full color) to 240 (white)
        //   S   Saturation   from 0 (gray) to 240 (full color)
        //
        // Due to its use of the color scaling operator this function expects to be
        // implemented in an Actor subclass.
        // ============================================================================
        public static Color HLStoRGB(int H, int L, int S)
        {
            int OffsetLightness;
            int OffsetSaturation;
            int R = 0;
            int G = 0;
            int B = 0;

            Color color;

            if (H < 0)
                H = (240 - H) % 240;
            else H = H % 240;

            if (H < 80)
                R = Math.Min(255, 255 * (80 - H) / 40);
            else
                if (H > 160)
                    R = Math.Min(255, 255 * (H - 160) / 40);
            if (H < 160)
                G = Math.Min(255, 255 * (80 - Math.Abs(H - 80)) / 40);
            if (H > 80)
                B = Math.Min(255, 255 * (80 - Math.Abs(H - 160)) / 40);

            if (S < 240)
            {
                R = (int)(R * ((float)S / 240));
                G = (int)(G * ((float)S / 240));
                B = (int)(B * ((float)S / 240));

                OffsetSaturation = 128 * (240 - S) / 240;
                R += OffsetSaturation;
                G += OffsetSaturation;
                B += OffsetSaturation;
            }

            L = Math.Min(240, L);

            R = (int)(R * ((120 - Math.Abs(L - 120)) / 120));
            G = (int)(G * ((120 - Math.Abs(L - 120)) / 120));
            B = (int)(B * ((120 - Math.Abs(L - 120)) / 120));

            if (L > 120)
            {
                OffsetLightness = 256 * (L - 120) / 120;
                R += OffsetLightness;
                G += OffsetLightness;
                B += OffsetLightness;
            }

            return color = Color.FromArgb(R, G, B);
        }
    }
}
